import qemuConnectionManager as qemu
import robusto as rob
import runManager as runMgr
import injector as inj
from rtems import RTEMSStackManager
import json
import validictory
import sys
import gdb

def start(jsonFile):
    with open(jsonFile, "r") as configFile:
        config = json.load(configFile)

    connectionManagers = {"qemu": qemu.QemuConnectionManager}
    targetOSs = {"rtems": RTEMSStackManager}
    injectors = ["stack", "symbol"]
    selfCorrectionCodes = {"robusto": rob.RobustoDetector}

    goldenRunSchema = {
        "type": "object",
        "properties": {
            "runType": {
                "type": "string",
                "pattern": "golden"
            },
            "injector": {
                "required": False
            }
        }
    }

    injectionRunSchema = {
        "type": "object",
        "properties": {
            "runType": {
                "type": "string",
                "pattern": "injection"
            },
            "injector": {
                "type": "string",
                "enum": injectors
            },
            "numberOfRuns": {
                "type": "integer",
                "minimum": 1
            }
        }
    }

    functionsToSkipSchema = {
        "type": "object",
        "properties": {
            "names": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "required": False
            },
            "regex": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "required": False
            }
        }
    }

    startAndEndPointsSchema = {
        "type": "array",
        "items": {
            "type": "string"
        },
        "minItems": 1
    }

    configSchema = {
        "type": "object",
        "properties": {
            "goldenRunFileName": {
                "type": "string"
            },
            "port": {
                "type": "integer",
                "minimum": 1
            },
            "connectionManager": {
                "type": "string",
                "enum": connectionManagers.keys()
            },
            "targetOS": {
                "type": "string",
                "enum": targetOSs.keys()
            },
            "runInfo": {
                "type": [goldenRunSchema, injectionRunSchema]
            },
            "selfCorrectionCode": {
                "type": "string",
                "enum": selfCorrectionCodes.keys()
            },
            "startPoints": startAndEndPointsSchema,
            "endPoints": startAndEndPointsSchema,
            "functionsToSkip": functionsToSkipSchema
        }
    }

    try:
        validictory.validate(config, configSchema)
    except:
        print("Configuration error: %s" % sys.exc_info()[1])
    else:
        # Clear any existing breakpoints.
        gdb.execute("delete")

        port = config["port"]
        connectionManager = connectionManagers[config["connectionManager"]](port)
        targetOSStackManager = targetOSs[config["targetOS"]]()

        if "selfCorrectionCode" in config:
            SCCDetector = selfCorrectionCodes[config["selfCorrectionCode"]]()
        else:
            SCCDetector = None

        goldenRunFileName = config["goldenRunFileName"]
        functionsToSkip = config["functionsToSkip"]
        startPoints = config["startPoints"]
        endPoints = config["endPoints"]
        runType = config["runInfo"]["runType"]

        if runType == "golden":
            runManager = runMgr.GoldenRunManager(connectionManager, SCCDetector, functionsToSkip)
            runManager.startRunning(goldenRunFileName, startPoints, endPoints)
        elif runType == "injection":
            numberOfRuns = config["runInfo"]["numberOfRuns"]
            injectorType = config["runInfo"]["injector"]

            if injectorType == "stack":
                injector = inj.StackInjector(targetOSStackManager)
            elif injectorType == "symbol":
                injector = inj.SymbolInjector()

            runManager = runMgr.InjectionRunManager(connectionManager, SCCDetector,
                                                    functionsToSkip, injector)
            runManager.startRunning(goldenRunFileName, numberOfRuns)

        gdb.execute("set logging off")

def log(string):
    gdb.execute("set logging off")
    print(string)
    gdb.execute("set logging on")


# Si le pongo *POSIX_Init en vez de POSIX_Init, me va a poner el breakpoint en la 1ra direccion de POSIX_Init.
# Para _User_extensions_Thread_exitted, y posiblemente para _Terminate, si trato de poner los breakpoints con
# el asterisco, me dice que no existe el simbolo. Pero si los pongo sin el asterisco, anda bien.
# Ver si puedo poner un exception handler o algo asi que se encargue de eso, por si el usuario la caga.

# Por ahora el targetOS lo usamos solamente para el stackInjector. Sin embargo, si mas adelante
# queremos soportar multithreading, vamos a necesitar info del OS, por lo que lo ponemos
# desde ya como campo obligatorio.
